<!--
 * @Description: 右击菜单，可通过组件方式引入，也可以通过函数调用
 * @Author: bxsdhx
 * @Date: 2023-09-18 14:43:53
 * @LastEditTime: 2024-07-12 18:06:13
 * Copyright (C) 2023 bxsdhx. All rights reserved.
-->
<template>
  <div
    ref="viewRef"
    class="right-click-menu"
    :style="{
      '--el-right-menu-item-height': menuItemHeight + 'px',
      '--el-right-menu-padding': menuPadding + 'px',
      height: menuHeight + 'px',
      top: menuTop + 'px',
      left: menuLeft + 'px',
      transform: `scaleY(${menuVisible ? 1 : 0})`,
      transformOrigin: menuOpenType,
    }"
    @transitionend="handleTransitionEnd"
    @contextmenu.prevent
  >
    <ElScrollbar>
      <div class="right-click-menu_list">
        <div
          :class="['right-click-menu_item', item.type, item.disabled ? 'disabled' : '']"
          v-for="item in menuList"
          :key="item.label"
          @mousedown="handleClick(item)"
        >
          {{ item.label }}
        </div>
      </div>
    </ElScrollbar>
  </div>
</template>
<script setup lang="ts">
import type { RightClickMenu } from "@/type";
import { reactive, ref, nextTick, useTemplateRef } from "vue";
import { ElScrollbar } from "element-plus";
import eventBus from "@/utils/eventBus";
const menuItemHeight = 32;
const menuPadding = 10;
const menuOpenType = ref<"top" | "bottom">("top");
const viewRef = useTemplateRef("viewRef");
const menuTop = ref<number>(0);
const menuLeft = ref<number>(0);
const menuHeight = ref<number>(0);
const menuVisible = ref<boolean>(false);
const menuList = reactive<RightClickMenu[]>([]);
const props = defineProps({
  close: {
    type: Function,
    default() {},
  },
});
/**
 * @description:  菜单点击
 * @param {RightClickMenu} item 当前子菜单
 * @return {void}
 */
const handleClick = function (item: RightClickMenu): void {
  if (item.disabled) return;
  if (typeof item.click === "function") {
    item.click(item);
    nextTick(handleClose);
  }
};
/**
 * @description: 打开菜单
 * @param {MouseEvent} event 鼠标事件
 * @param {RightClickMenuList} list 菜单列表
 * @return {void}
 */
const handleOpen = function (event: MouseEvent, list: RightClickMenu[] = []): void {
  menuList.splice(0, menuList.length, ...list.filter((e) => !e.hidden));
  nextTick(function () {
    if (!viewRef.value) return;
    const height = menuList.length * menuItemHeight + menuPadding * 2;
    if (event.clientY + height <= window.innerHeight) {
      menuHeight.value = height + 2;
      menuTop.value = event.clientY;
      menuOpenType.value = "top";
    } else if (event.clientY + height > window.innerHeight) {
      menuTop.value = Math.max(0, event.clientY - height);
      menuHeight.value = Math.min(window.innerHeight - menuTop.value, height) + 2;
      menuOpenType.value = "bottom";
    }
    const centerX = viewRef.value.offsetWidth / 2;
    menuLeft.value = Math.min(window.innerWidth - centerX, Math.max(0, event.clientX - centerX));
    nextTick(function () {
      menuVisible.value = true;
    });
  });
};
/**
 * @description: 关闭菜单
 * @return {void}
 */
const handleClose = function (): void {
  menuVisible.value = false;
};
const handleTransitionEnd = function () {
  if (!menuVisible.value) {
    props.close();
  }
};
// 点击其他区域关闭菜单
eventBus.addEventListener("mousedown", handleClose);
/**
 * 暴露函数
 */
defineExpose({
  /**
   * @description: 打开菜单
   * @param {MouseEvent} event 鼠标事件
   * @param {RightClickMenuList} list 菜单列表
   * @return {void}
   */
  handleOpen,
  /**
   * 关闭菜单
   */
  handleClose,
});
</script>
<style scoped lang="scss">
.right-click-menu {
  position: fixed;
  top: 0;
  left: 0;
  z-index: 99999999;
  background-color: var(--el-bg-color-overlay);
  border: 1px solid var(--el-border-color-light);
  border-radius: var(--el-border-radius-base);
  overflow: hidden;
  box-shadow: var(--el-box-shadow-light);
  transition: transform var(--el-transition-duration-fast) linear;
  &_item {
    box-sizing: border-box;
    width: 100%;
    height: var(--el-right-menu-item-height);
    white-space: nowrap;
    padding: 0 16px;
    font-size: var(--el-font-size-base);
    color: var(--el-text-color-regular);
    cursor: pointer;
    user-select: none;
    display: flex;
    align-items: center;
    &.primary {
      &:hover {
        color: var(--el-color-primary);
        background-color: var(--el-color-primary-light-9);
      }
    }
    &.danger {
      color: var(--el-color-danger);
      &:hover {
        background-color: var(--el-color-danger-light-9);
      }
    }
    &.disabled {
      cursor: not-allowed;
      color: var(--el-text-color-disabled);
      &:hover {
        background-color: transparent;
        color: var(--el-text-color-disabled);
      }
    }
    .annex {
      margin-left: 16px;
    }
  }
  &_list {
    padding: var(--el-right-menu-padding) 0;
  }
}
</style>
